﻿' ******************************************************************************
' ** 
' **  Yahoo Finance Managed
' **  Written by Marius Häusler 2010
' **  It would be pleasant, if you contact me when you are using this code.
' **  Contact: YahooFinanceManaged@gmail.com
' **  Project Home: http://code.google.com/p/yahoo-finance-managed/
' **  
' ******************************************************************************
' **  
' **  Copyright 2010 Marius Häusler
' **  
' **  Licensed under the Apache License, Version 2.0 (the "License");
' **  you may not use this file except in compliance with the License.
' **  You may obtain a copy of the License at
' **  
' **    http://www.apache.org/licenses/LICENSE-2.0
' **  
' **  Unless required by applicable law or agreed to in writing, software
' **  distributed under the License is distributed on an "AS IS" BASIS,
' **  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
' **  See the License for the specific language governing permissions and
' **  limitations under the License.
' ** 
' ******************************************************************************


Namespace Finance.API

    ''' <summary>
    ''' Provides methods for downloading quotes data.
    ''' </summary>
    ''' <remarks></remarks>
    Public Class QuotesDownload
        Inherits Base.Download
        Implements Base.IStringDownload

        ''' <summary>
        ''' Raises if an asynchronous download of quotes data completes.
        ''' </summary>
        ''' <param name="sender">The event raising object</param>
        ''' <param name="ea">The event args of the asynchronous download</param>
        ''' <remarks></remarks>
        Public Event AsyncDownloadCompleted(ByVal sender As Base.Download, ByVal ea As QuotesDownloadCompletedEventArgs)

        Friend ReadOnly mFinanceHelper As New FinanceHelper
        Private mCsvParser As New ImportExport.CSV
        Private mXmlParser As New ImportExport.XML
        Private mTextEncoding As System.Text.Encoding = System.Text.Encoding.Default

        ''' <summary>
        ''' The text encoding for CSV download.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property TextEncoding() As System.Text.Encoding Implements Base.IStringDownload.TextEncoding
            Get
                Return mTextEncoding
            End Get
            Set(ByVal value As System.Text.Encoding)
                mTextEncoding = value
            End Set
        End Property

        ''' <summary>
        ''' Downloads quotes data.
        ''' </summary>
        ''' <param name="managedID">The managed ID</param>
        ''' <param name="properties">The properties of each quote data. If parameter is null/Nothing, Symbol and LastTradePrizeOnly will set as property. In this case, with YQL server you will get every available property.</param>
        ''' <param name="server">The used server for downloading</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Overloads Function Download(ByVal managedID As IID, ByVal properties As IEnumerable(Of QuoteProperty), ByVal server As Server) As QuotesResponse
            If managedID Is Nothing Then Throw New ArgumentNullException("id", "The passed id is null.")
            Return Me.Download(managedID.ID, properties, server)
        End Function
        ''' <summary>
        ''' Downloads quotes data.
        ''' </summary>
        ''' <param name="unmanagedID">The unmanaged ID</param>
        ''' <param name="properties">The properties of each quote data. If parameter is null/Nothing, Symbol and LastTradePrizeOnly will set as property. In this case, with YQL server you will get every available property.</param>
        ''' <param name="server">The used server for downloading</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Overloads Function Download(ByVal unmanagedID As String, ByVal properties As IEnumerable(Of QuoteProperty), ByVal server As Server) As QuotesResponse
            If unmanagedID = String.Empty Then Throw New ArgumentNullException("unmanagedID", "The passed id is empty.")
            Return Me.Download(New String() {unmanagedID}, properties, server)
        End Function
        ''' <summary>
        ''' Downloads quotes data.
        ''' </summary>
        ''' <param name="managedIDs">The list of managed IDs</param>
        ''' <param name="properties">The properties of each quote data. If parameter is null/Nothing, Symbol and LastTradePrizeOnly will set as property. In this case, with YQL server you will get every available property.</param>
        ''' <param name="server">The used server for downloading</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Overloads Function Download(ByVal managedIDs As IEnumerable(Of IID), ByVal properties As IEnumerable(Of QuoteProperty), ByVal server As Server) As QuotesResponse
            If managedIDs Is Nothing Then Throw New ArgumentNullException("managedIDs", "The passed list is null.")
            Return Me.Download(mFinanceHelper.IIDsToStrings(managedIDs), properties, server)
        End Function
        ''' <summary>
        ''' Downloads quotes data.
        ''' </summary>
        ''' <param name="unmanagedIDs">The list of unmanaged IDs</param>
        ''' <param name="properties">The properties of each quote data. If parameter is null/Nothing, Symbol and LastTradePrizeOnly will set as property. In this case, with YQL server you will get every available property.</param>
        ''' <param name="server">The used server for downloading</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Overloads Function Download(ByVal unmanagedIDs As IEnumerable(Of String), ByVal properties As IEnumerable(Of QuoteProperty), ByVal server As Server) As QuotesResponse
            If unmanagedIDs Is Nothing Then Throw New ArgumentNullException("unmanagedIDs", "The passed list is null.")
            Dim prps() As QuoteProperty = mFinanceHelper.CheckProperties(properties)
            Return Me.ToResponse(MyBase.Download(Me.DownloadURL(mHelper.EnumToArray(unmanagedIDs), CType(IIf(server = YahooManaged.Server.YQL, mHelper.EnumToArray(properties), prps), QuoteProperty()), server)), server, prps)
        End Function

        ''' <summary>
        ''' Starts an asynchronous download of quotes data.
        ''' </summary>
        ''' <param name="managedID">The managed ID</param>
        ''' <param name="properties">The properties of each quote data. If parameter is null/Nothing, Symbol and LastTradePrizeOnly will set as property. In this case, with YQL server you will get every available property.</param>
        ''' <param name="server">The used server for downloading</param>
        ''' <param name="userArgs">Individual user argument</param>
        ''' <remarks></remarks>
        Public Overloads Sub DownloadAsync(ByVal managedID As IID, ByVal properties As IEnumerable(Of QuoteProperty), ByVal server As Server, Optional ByVal userArgs As Object = Nothing)
            If managedID Is Nothing Then Throw New ArgumentNullException("managedID", "The passed ID is null.")
            Me.DownloadAsync(managedID.ID, properties, server, userArgs)
        End Sub
        ''' <summary>
        ''' Starts an asynchronous download of quotes data.
        ''' </summary>
        ''' <param name="unmanagedID">The unmanaged ID</param>
        ''' <param name="properties">The properties of each quote data. If parameter is null/Nothing, Symbol and LastTradePrizeOnly will set as property. In this case, with YQL server you will get every available property.</param>
        ''' <param name="server">The used server for downloading</param>
        ''' <param name="userArgs">Individual user argument</param>
        ''' <remarks></remarks>
        Public Overloads Sub DownloadAsync(ByVal unmanagedID As String, ByVal properties As IEnumerable(Of QuoteProperty), ByVal server As Server, Optional ByVal userArgs As Object = Nothing)
            If unmanagedID = String.Empty Then Throw New ArgumentNullException("unmanagedID", "The passed ID is empty.")
            Me.DownloadAsync(New String() {unmanagedID}, properties, server, userArgs)
        End Sub
        ''' <summary>
        ''' Starts an asynchronous download of quotes data.
        ''' </summary>
        ''' <param name="managedIDs">The list of managed IDs</param>
        ''' <param name="properties">The properties of each quote data. If parameter is null/Nothing, Symbol and LastTradePrizeOnly will set as property. In this case, with YQL server you will get every available property.</param>
        ''' <param name="server">The used server for downloading</param>
        ''' <param name="userArgs">Individual user argument</param>
        ''' <remarks></remarks>
        Public Overloads Sub DownloadAsync(ByVal managedIDs As IEnumerable(Of IID), ByVal properties As IEnumerable(Of QuoteProperty), ByVal server As Server, Optional ByVal userArgs As Object = Nothing)
            If managedIDs Is Nothing Then Throw New ArgumentNullException("managedIDs", "The passed list is null.")
            Me.DownloadAsync(mFinanceHelper.IIDsToStrings(managedIDs), properties, server, userArgs)
        End Sub
        ''' <summary>
        ''' Starts an asynchronous download of quotes data.
        ''' </summary>
        ''' <param name="unmanagedIDs">The list of unmanaged IDs</param>
        ''' <param name="properties">The properties of each quote data. If parameter is null/Nothing, Symbol and LastTradePrizeOnly will set as property. In this case, with YQL server you will get every available property.</param>
        ''' <param name="server">The used server for downloading</param>
        ''' <param name="userArgs">Individual user argument</param>
        ''' <remarks></remarks>
        Public Overloads Sub DownloadAsync(ByVal unmanagedIDs As IEnumerable(Of String), ByVal properties As IEnumerable(Of QuoteProperty), ByVal server As Server, Optional ByVal userArgs As Object = Nothing)
            If unmanagedIDs Is Nothing Then Throw New ArgumentNullException("unmanagedIDs", "The passed list is null.")
            Dim args As New AsyncDownloadArgs(userArgs, mHelper.EnumToArray(unmanagedIDs), mFinanceHelper.CheckProperties(properties), server)
            MyBase.DownloadAsync(Me.DownloadURL(args.IDs, args.Properties, args.Server), args)
        End Sub

        ''' <summary>
        ''' Default constructor
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub New()
        End Sub

        Private Sub DownloadAsync_Completed(ByVal sender As Base.Download, ByVal ba As Base.StreamDownloadCompletedEventArgs) Handles MyBase.AsyncStreamDownloadCompleted
            If ba IsNot Nothing AndAlso ba.UserArgs IsNot Nothing AndAlso TypeOf ba.UserArgs Is AsyncDownloadArgs Then
                Dim dlArgs As AsyncDownloadArgs = DirectCast(ba.UserArgs, AsyncDownloadArgs)
                Dim args As New QuotesDownloadCompletedEventArgs(dlArgs.UserArgs, Me.ToResponse(ba.Response, dlArgs.Server, dlArgs.Properties), dlArgs.IDs, dlArgs.Properties, dlArgs.Server)
                RaiseEvent AsyncDownloadCompleted(Me, args)
            End If
        End Sub

        Private Function DownloadURL(ByVal unmanagedIDs As IEnumerable(Of String), ByVal properties() As QuoteProperty, ByVal server As Server) As String
            Dim lst() As String = mHelper.EnumToArray(unmanagedIDs)
            If lst.Length > 0 Then

                If server = server.YQL Then
                    Dim fields As String = String.Empty
                    If properties Is Nothing OrElse properties.Length = 0 Then
                        fields = "*"
                    Else
                        Dim sb As New Text.StringBuilder
                        For Each p As QuoteProperty In properties
                            sb.Append(p.ToString)
                            sb.Append(",")
                        Next
                        sb.Remove(sb.Length - 1, 1)
                        fields = sb.ToString
                    End If

                    Dim whereClause As New Text.StringBuilder()
                    whereClause.Append("symbol in (")
                    For Each id As String In lst
                        whereClause.Append(""""c)
                        whereClause.Append(mHelper.CleanYqlParam(id))
                        whereClause.Append(""",")
                    Next
                    whereClause.Remove(whereClause.Length - 1, 1)
                    whereClause.Append(")"c)

                    Return mHelper.YqlUrl(fields, "yahoo.finance.quotes", whereClause.ToString, Nothing)

                Else
                    Dim ids As New Text.StringBuilder
                    For Each s As String In lst
                        ids.Append(mHelper.CleanYqlParam(s))
                        ids.Append("+"c)
                    Next
                    Dim srv As String = mHelper.ServerString(server)
                    Return "http://" & IIf(srv <> String.Empty, srv, "download.").ToString & "finance.yahoo.com/d/quotes.csv?s=" & Uri.EscapeDataString(ids.ToString) & "&f=" & mFinanceHelper.CsvQuotePropertyTags(properties) & "&e=.csv"
                End If

            Else
                Throw New NotSupportedException("An empty id list will not be supported.")
            End If
        End Function

        Private Function ToResponse(ByVal resp As Base.StreamResponse, ByVal server As Server, ByVal properties() As QuoteProperty) As QuotesResponse
            Dim quotes As New List(Of QuoteData)
            If server = server.YQL Then
                Dim culture As New Globalization.CultureInfo("en-US")
                Dim results() As XmlNode = mHelper.GetResultsFromXml(resp.Result, "quote")
                For Each node As XmlNode In results
                    Dim q As QuoteData = mXmlParser.ToQuoteData(node, culture)
                    If q IsNot Nothing Then quotes.Add(q)
                Next
            Else
                quotes.AddRange(mCsvParser.ToQuotesData(mHelper.StreamToString(resp.Result, mTextEncoding), mFinanceHelper.ServerToDelimiter(server), properties, mFinanceHelper.ServerToCulture(server)))
            End If
            Return New QuotesResponse(resp.Connection, quotes.ToArray)
        End Function

        Private Class AsyncDownloadArgs
            Inherits Base.DownloadEventArgs
            Public IDs() As String
            Public Properties() As QuoteProperty
            Public Server As Server
            Public Sub New(ByVal userArgs As Object, ByVal id() As String, ByVal prps() As QuoteProperty, ByVal srv As Server)
                MyBase.New(userArgs)
                Me.IDs = id
                Me.Properties = prps
                Me.Server = srv
            End Sub
        End Class

    End Class

    ''' <summary>
    ''' Stores the received quotes information of an asynchronous download
    ''' </summary>
    ''' <remarks></remarks>
    Public Class QuotesDownloadCompletedEventArgs
        Inherits Base.DownloadCompletedEventArgs

        Private mIDs() As String
        Private mProperties() As QuoteProperty
        Private mServer As Server

        Public ReadOnly Property IDs() As IEnumerable(Of String)
            Get
                Return mIDs
            End Get
        End Property
        ''' <summary>
        ''' Gets the properties that were queried for each quotes data instance
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Properties() As QuoteProperty()
            Get
                Return mProperties
            End Get
        End Property
        ''' <summary>
        ''' Gets the used server for downloading
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Server() As Server
            Get
                Return mServer
            End Get
        End Property
        ''' <summary>
        ''' Gets the response with quotes data.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Overloads ReadOnly Property Response() As QuotesResponse
            Get
                Return DirectCast(MyBase.Response, QuotesResponse)
            End Get
        End Property

        Friend Sub New(ByVal userArgs As Object, ByVal resp As QuotesResponse, ByVal ids() As String, ByVal properties() As QuoteProperty, ByVal server As Server)
            MyBase.New(userArgs, resp)
            mIDs = ids
            mProperties = properties
            mServer = server
        End Sub

    End Class

    ''' <summary>
    ''' Provides information and response of an asynchronous quotes download.
    ''' </summary>
    ''' <remarks></remarks>
    Public Class QuotesResponse
        Inherits Base.Response

        ''' <summary>
        ''' Gets the received quotes data.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Overloads ReadOnly Property Result() As QuoteData()
            Get
                Return TryCast(MyBase.Result, QuoteData())
            End Get
        End Property

        Friend Sub New(ByVal info As Base.ConnectionInfo, ByVal result As QuoteData())
            MyBase.New(info, result)
        End Sub

    End Class

End Namespace